package per.hnvcam.util.console.ssh;

import org.apache.sshd.ClientChannel;
import org.apache.sshd.ClientSession;
import org.apache.sshd.SshClient;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.session.ClientSessionImpl;
import org.apache.sshd.common.util.NoCloseInputStream;
import org.apache.sshd.common.util.NoCloseOutputStream;

import per.hnvcam.util.console.ClientSupport;
import per.hnvcam.util.console.ConsoleInputStream;
import per.hnvcam.util.console.ConsoleOutputStream;

public class SSHClientSupport implements Runnable, ClientSupport {

   private SshClient sshclient;
   private ConsoleInputStream inputStream;
   private ConsoleOutputStream outputStream;
   private ConsoleOutputStream errorStream;
   private String host;
   private int port;
   private String username;
   private String password;
   private ClientSession session;
   private Thread daemonThread;
   private NoCloseChannelShell channel;
   private boolean clientIsSetUp;

   public SSHClientSupport() {
      inputStream = new ConsoleInputStream();
      outputStream = new ConsoleOutputStream();
      errorStream = new ConsoleOutputStream();
      clientIsSetUp = false;
   }

   public void connect(String host, int port, String username, String password) {
      this.host = host;
      this.port = port;
      this.username = username;
      this.password = password;
      daemonThread = new Thread(this);
      daemonThread.setDaemon(true);
      daemonThread.start();
   }

   public ConsoleInputStream getInputStream() {
      return inputStream;
   }

   public ConsoleOutputStream getOutputStream() {
      return outputStream;
   }

   public void run() {
      try {
         sshclient = SshClient.setUpDefaultClient();
         sshclient.start();
         ConnectFuture connectFuture = sshclient.connect(host, port);
         while (!connectFuture.isConnected()) {
            Thread.sleep(1000);
         }
         session = connectFuture.getSession();
         int ret = ClientSession.WAIT_AUTH;
         while ((ret & ClientSession.WAIT_AUTH) != 0) {
            session.authPassword(username, password);
            ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
         }
         if ((ret & ClientSession.CLOSED) != 0) {
            return;
         }
         channel = new NoCloseChannelShell();
         ((ClientSessionImpl) session).registerChannel(channel);
         channel.setIn(new NoCloseInputStream(inputStream));
         channel.setOut(new NoCloseOutputStream(outputStream));
         channel.setErr(new NoCloseOutputStream(errorStream));
         clientIsSetUp = true;
         channel.open();
         channel.waitFor(ClientChannel.CLOSED, 0);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   public void close() {
      channel.close(true);
      session.close(false);
      sshclient.stop();
   }

   public boolean isOpened() {
      return clientIsSetUp;
   }

   public ConsoleOutputStream getErrorStream() {
      return errorStream;
   }
}
